#pragma once

namespace Core {
	template<class T>
	class Vector3_t {
	public: // Typedefs
		typedef T value_type;
		typedef Vector3_t<T> this_type;

	public: // Constructors
		Vector3_t();
		Vector3_t( T x, T y, T z );

		// Convert to other Vector3-type (forbid implicit conversions)
		template<class U>
		explicit Vector3_t( const Vector3_t<U>& vec ) :
			x( (T)vec.x ),
			y( (T)vec.y ),
			z( (T)vec.z ) {
		}

	public: // Named constructors
		Vector3_t<T> formUniform( T u );

	public: // Operators
		const Vector3_t<T> operator- () const;
		const Vector3_t<T> operator+ ( const Vector3_t<T>& vec ) const;
		const Vector3_t<T> operator- ( const Vector3_t<T>& vec ) const;
		const Vector3_t<T> operator* ( const Vector3_t<T>& vec ) const;
		const Vector3_t<T> operator/ ( const Vector3_t<T>& vec ) const;
		const Vector3_t<T> operator* ( T scale ) const;
		const Vector3_t<T> operator/ ( T scale ) const;
		
		Vector3_t<T>& operator+= ( const Vector3_t<T>& vec );
		Vector3_t<T>& operator-= ( const Vector3_t<T>& vec );
		Vector3_t<T>& operator*= ( const Vector3_t<T>& vec );
		Vector3_t<T>& operator/= ( const Vector3_t<T>& vec );
		Vector3_t<T>& operator*= ( T scale );
		Vector3_t<T>& operator/= ( T scale );

	public: // Methods
		T dot( const this_type& vec ) const;
		T sqLength() const;
		T length() const;
		Vector3_t<T>& normalize();
		const Vector3_t<T> normalized() const;
		const Vector3_t<T> cross( const this_type& b ) const;

	public: // Accessors
		T& operator [] ( int idx );
		const T& operator [] ( int idx ) const;

	private: // Helper members
		static T Vector3_t::* const (map[3]);

	public: // Members
		T x, y, z;
	};

	// Free functions for "constant (op) vector" notation
	template<class T>
	const Vector3_t<T> operator * ( T scale, const Vector3_t<T>& vec );

	template<class T>
	const Vector3_t<T> operator / ( T scale, const Vector3_t<T>& vec );

	// Typedefs available to the Core namespace
	typedef Vector3_t<float> Vector3f;
	typedef Vector3_t<double> Vector3;
	typedef Vector3_t<int> Vector3i;
}